﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using AL.Doodled.Compiling;
using System.IO;
using Microsoft.Xna.Framework;

namespace AL.Doodled
{
    public class DoodledEngine
    {
        #region fields
        private Game game;
        private Compiler compiler;
        private WidgetStyleRegister wsregister;
        private Dictionary<string, dynamic> globals;
        private Dictionary<string, SpriteFont> fonts;
        private Dictionary<string, Texture2D> textures;
        #endregion

        #region properties
        internal List<string> TypePaths { get; private set; }
        internal Compiler Compiler { get { return compiler; } }
        internal WidgetStyleRegister WSRegister { get { return wsregister; } }
        #endregion

        #region initialization
        public DoodledEngine(Game game)
        {
            DataExtractor.SetEngine(this);

            this.game = game;

            wsregister = new WidgetStyleRegister();
            TypePaths = new List<string>();
            globals = new Dictionary<string, dynamic>();
            fonts = new Dictionary<string, SpriteFont>();
            textures = new Dictionary<string, Texture2D>();
            compiler = new Compiler(this);

            TypePaths.Add("AL.Doodled.Widgets.");


            Texture2D defTex = new Texture2D(game.GraphicsDevice,
                1, 1);
            defTex.SetData(new[] { Color.White });
            textures.Add("default", defTex);
        }
        #endregion

        #region methods

        #region fonts
        /// <summary>
        /// Get the value of a font.
        /// </summary>
        /// <param name="name">The font name.</param>
        /// <returns>The SpriteFont.</returns>
        internal SpriteFont GetFont(string name)
        {
            if (fonts.ContainsKey(name))
                return fonts[name];
            else
                throw new KeyNotFoundException("There is no such font");
        }

        /// <summary>
        /// Set the value of a font and define it if it is undefined.
        /// </summary>
        /// <param name="name">The font name.</param>
        /// <param name="value">The Font instance.</param>
        internal void SetFont(string name, SpriteFont value)
        {
            if (fonts.ContainsKey(name))
                fonts[name] = value;
            else
                fonts.Add(name, value);
        }

        /// <summary>
        /// Evaluate if there is such a font in the engine.
        /// </summary>
        /// <param name="name">The name of the font.</param>
        /// <returns>Is there such a font defined?</returns>
        internal bool IsFontDefined(string name)
        {
            return fonts.ContainsKey(name);
        }
        #endregion

        #region textures
        /// <summary>
        /// Get the value of a Texture.
        /// </summary>
        /// <param name="name">The Texture name.</param>
        /// <returns>The Texture2D.</returns>
        internal Texture2D GetTexture(string name)
        {
            if (textures.ContainsKey(name))
                return textures[name];
            else
                throw new KeyNotFoundException("There is no such Texture");
        }

        /// <summary>
        /// Set the value of a Texture and define it if it is undefined.
        /// </summary>
        /// <param name="name">The Texture name.</param>
        /// <param name="value">The Texture instance.</param>
        internal void SetTexture(string name, Texture2D value)
        {
            if (textures.ContainsKey(name))
                textures[name] = value;
            else
                textures.Add(name, value);
        }

        /// <summary>
        /// Evaluate if there is such a Texture in the engine.
        /// </summary>
        /// <param name="name">The name of the Texture.</param>
        /// <returns>Is there such a Texture defined?</returns>
        internal bool IsTextureDefined(string name)
        {
            return textures.ContainsKey(name);
        }
        #endregion

        #region globals
        /// <summary>
        /// Get a global variables value.
        /// </summary>
        /// <param name="name">The name of the global.</param>
        /// <returns>The value of the global.</returns>
        public dynamic GetGlobal(string name)
        {
            if (globals.ContainsKey(name))
                return globals[name];
            else
                throw new KeyNotFoundException("There is no such global defined");
        }

        /// <summary>
        /// Get the value of the global as a string, either as a xml
        /// string if the global implements the interface IXMLSupported
        /// else the ToString() function is called.
        /// </summary>
        /// <param name="name">The name of the global.</param>
        /// <returns>The data string returned from the global.</returns>
        public string GetGlobalData(string name)
        {
            dynamic data = GetGlobal(name);
            if (data is IXMLSupported)
                return data.ToXml();
            else
                return data.ToString();
        }

        /// <summary>
        /// Assign a globals value, define it if undefined.
        /// </summary>
        /// <param name="name">The name of the global we are assigning.</param>
        /// <param name="value">The value we are to assign to the global.</param>
        public void SetGlobal(string name, dynamic value)
        {
            if (globals.ContainsKey(name))
                globals[name] = value;
            else
                globals.Add(name, value);
        }

        /// <summary>
        /// Evaluate if there is any global with this name.
        /// </summary>
        /// <param name="name">The name of the global.</param>
        /// <returns>Is the global defined?</returns>
        public bool IsGlobalDefined(string name)
        {
            return globals.ContainsKey(name);
        }

        /// <summary>
        /// Defines all default globals for the engine.
        /// </summary>
        private void defineGlobals()
        {
        }
        #endregion

        #region loading
        /// <summary>
        /// Load a drs file and extract its data.
        /// </summary>
        /// <param name="path">The file path.</param>
        public void LoadResourceFile(string path)
        {
            using (StreamReader r = new StreamReader(path))
                compiler.ParseResourceData(r.ReadToEnd(), 
                    game.Content, game.GraphicsDevice);
        }

        /// <summary>
        /// Loads a drs code string and extract its data.
        /// </summary>
        /// <param name="code">The drs code.</param>
        public void LoadResourceData(string code)
        {
            compiler.ParseResourceData(code, 
                game.Content, game.GraphicsDevice);
        }

        /// <summary>
        /// Loads a register code string and extract its data.
        /// </summary>
        /// <param name="code">The register code string.</param>
        public void LoadRegisterData(string code)
        {
            compiler.ParseRegisterData(code);
        }
        #endregion

        #endregion
    }
}
